package jdt.gui;

import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import jdt.DelaunayTriangulation;
import jdt.geom.ConvexHull;
import jdt.geom.Edge;
import jdt.geom.Point;
import jdt.geom.Triangle;

@SuppressWarnings("serial")
public class TriangulationGui extends Frame {

	/* --- Data members --- */

	/** The dimension of the board */
	private static final int WIDTH = 1000;
	private static final int HEIGHT = 1000;

	/** All the point which has to be drawn */
	private List<Point> allPoints = new LinkedList<Point>();

	/* --- Ctors. --- */

	/**
	 * Construct an instance of the GUI
	 */
	public TriangulationGui() {
		addWindowListener(new JdtWindowsListener());
		addMouseListener(new JdtMouseListener(this));
	}

	/* --- Public Methods --- */

	/**
	 * Runs the application
	 */
	public void start() {
		setVisible(true);
		setBounds(0, 0, WIDTH, HEIGHT);
	}

	@Override
	public void paint(Graphics g) {
		synchronized (this) {
			// Draw the triangulation
			DelaunayTriangulation tri = new DelaunayTriangulation();
			for (Point point : allPoints) {
				tri.insertPoint(point);
			}

			Set<Triangle> triangulation = tri.getTriangulation();
			for (Triangle triangle : triangulation) {
				drawTriangle(triangle);
			}

			drawConvexHull();
		}
	}

	/**
	 * Add a new point to paint
	 * 
	 * @param p
	 *            The point to add
	 */
	public synchronized void addPoint(Point p) {
		allPoints.add(guiToWorld(p));
	}

	/* --- Private Methods --- */

	/**
	 * Draw the convex hull of all the points
	 */
	private void drawConvexHull() {
		ConvexHull convexHull = new ConvexHull(allPoints);
		Set<Point> convexHullPoints = convexHull.getConvexHullPoints();
		Set<Edge> convexHullEdges = convexHull.getConvexHullEdges(); 
		
		for (Point point : convexHullPoints) {
			drawPoint(point, Color.RED);
		}
		
		for (Edge edge : convexHullEdges) {
			drawLine(edge.getPoints().get(0), edge.getPoints().get(1), Color.RED);
		}
	}

	private void drawTriangle(Triangle triangle) {
		drawLine(triangle.getA(), triangle.getB());
		drawLine(triangle.getB(), triangle.getC());
		drawLine(triangle.getC(), triangle.getA());
	}

	private void drawLine(Point start, Point end) {
		drawLine(start, end, Color.BLACK);
	}
	
	private void drawLine(Point start, Point end, Color color) {
		Point guiStart = guiToWorld(start);
		Point guiEnd = guiToWorld(end);
		Graphics g = getGraphics();
		g.setColor(color);
		g.drawLine((int) guiStart.getX(), (int) guiStart.getY(), (int) guiEnd.getX(),
				(int) guiEnd.getY());
	}

	private void drawPoint(Point p, Color color) {
		Point toPaint = guiToWorld(p);
		Graphics g = getGraphics();
		g.setColor(color);
//		g.drawString(p.toString(), (int)toPaint.getX(), (int)toPaint.getY()+15);
		g.fillOval((int) toPaint.getX(), (int) toPaint.getY(), 5, 5);
	}

	/* --- Public Static Methods --- */

	/**
	 * Convert from GUI coordinate system to WORLD coordinate system
	 * @param p The point to convert
	 * @return the converted point
	 */
	public static Point guiToWorld(Point p){
		return new Point(p.getX(), HEIGHT - p.getY());
	}
	
	/**
	 * The application entry point
	 * 
	 * @param args
	 *            the command line arguments
	 */
	public static void main(String[] args) {
		new TriangulationGui().start();
	}
}
